#include "stdafx.h"

#ifdef SUPPORT_H264

#include <streams.h>
#include <stdio.h>

#include "DecodeFilter.h"

TCHAR FILTER_NAME[] = TEXT("Coship H264 Decoder");

//
// Constructor
//
H264DecodeFilter::H264DecodeFilter(TCHAR *tszName,LPUNKNOWN punk,HRESULT *phr) :
    CTransformFilter(FILTER_NAME, punk, CLSID_NULL),
	m_session(0),
	_isPaused(false),
	_willStepForward(false),
	_lastBitrateCountTime(0),
	_lastBitrate(0),
	_totalBit(0),
	_lastActualFps(0),
	_totalFrame(0)
{
    //ASSERT(tszName);
    ASSERT(phr);
	AddRef();

} // 

H264DecodeFilter::~H264DecodeFilter()
{
	//if (m_State != State_Stopped)
	//	Stop();

	if (m_session)
		CloseDecodeSession(m_session);
};

STDMETHODIMP H264DecodeFilter::Stop()
{
	ContinuePlayback();

	return CTransformFilter::Stop();
}

void H264DecodeFilter::PausePlayback()
{
	CAutoLock lck(&m_csFilter);
	if (m_State == State_Stopped)
		return;

	_isPaused = true;
	_willStepForward = false;
}

void H264DecodeFilter::ContinuePlayback()
{
	_isPaused = false;
	_willStepForward = false;
}

void H264DecodeFilter::StepForward()
{
	if (!_isPaused)
		return;

	_willStepForward = true;
}

//
// CreateInstance
//
// Provide the way for COM to create a H264DecodeFilter object
//
CUnknown * WINAPI H264DecodeFilter::CreateInstance(LPUNKNOWN punk, HRESULT *phr) 
{
    ASSERT(phr);
    
    H264DecodeFilter *pNewObject = new H264DecodeFilter(NAME("Contrast"), punk, phr);
    if (pNewObject == NULL) {
        if (phr)
            *phr = E_OUTOFMEMORY;
    }

    return pNewObject;

} // CreateInstance


//
// NonDelegatingQueryInterface
//
// Reveals IContrast and ISpecifyPropertyPages
//
STDMETHODIMP H264DecodeFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
    CheckPointer(ppv,E_POINTER);

    return CTransformFilter::NonDelegatingQueryInterface(riid, ppv);

} // NonDelegatingQueryInterface

int H264DecodeFilter::GetBitrate()
{
	if (timeGetTime() - _lastBitrateCountTime > 2 * BITRATE_COUNT_INTERVAL)
		return 0;
	else
		return _lastBitrate;
}

//
// Transform
//
// Copy the input sample into the output sample
// Then transform the output sample 'in place'
//
HRESULT H264DecodeFilter::Transform(IMediaSample *pIn, IMediaSample *pOut)
{
	while (_isPaused && !_willStepForward)
		Sleep(5);

	_willStepForward = false;

	VIDEOINFO *info = (VIDEOINFO *) m_intype.pbFormat;

	info = (VIDEOINFO *) m_outtype.pbFormat;
	int offset = abs(info->bmiHeader.biWidth) * abs(info->bmiHeader.biHeight);
	BYTE *inbuf, *outbuf;
	pIn->GetPointer(&inbuf);
	pOut->GetPointer(&outbuf);
	int size = pIn->GetSize();

	if (_lastBitrateCountTime == 0)
		_lastBitrateCountTime = timeGetTime();

	_totalBit += size * 8;
	_totalFrame++;
	DWORD now = timeGetTime();
	if (now < _lastBitrateCountTime)
	{
		_lastBitrateCountTime = now;
		_totalBit = 0;
		_totalFrame = 0;
	}
	if (now - _lastBitrateCountTime >= BITRATE_COUNT_INTERVAL)
	{
		_lastBitrate = _totalBit * 1000 / (now - _lastBitrateCountTime);
		_lastActualFps = (DWORD) (double(_totalFrame) * 1000.0 / double(now - _lastBitrateCountTime) + 0.5);
		_lastBitrateCountTime = now;
		_totalBit = 0;
		_totalFrame = 0;
	}

	if (m_session == 0)
	{
		m_session = CreateDecodeSessionNoSize(0);
		//m_session = CreateDecodeSession(abs(info->bmiHeader.biWidth), abs(info->bmiHeader.biHeight), 0);
	}

	if (m_session != 0)
	{
		int getframe = 0;
		#define DATA_SIZE 4096
		while (size > 0)
		{
			 int len = DecodeFrame(m_session, inbuf, (UINT) size,
				outbuf, outbuf + offset + offset / 4, outbuf + offset, 
				info->bmiHeader.biWidth, 0);
			 if (len < 0)
				 break;
			 inbuf += len;
			 size = size - len;

			 //int width, height;
			 //GetVideoSize(m_session, &width, &height);
			 //char msg[128];
			 //sprintf(msg, "width = %d, height = %d, getframe = %d\n", width, height, getframe);
			 //OutputDebugStr(msg);
		}

	}

	return NOERROR;
} // Transform


//
// CheckInputType
//
// Check the input type is OK, return an error otherwise
//
HRESULT H264DecodeFilter::CheckInputType(const CMediaType *mtIn)
{
    CheckPointer(mtIn,E_POINTER);

    // Check this is a VIDEOINFO type

    if(*mtIn->FormatType() != FORMAT_VideoInfo)
    {
        return E_INVALIDARG;
    }

	VIDEOINFO &info = *(VIDEOINFO *) mtIn->Format();
	if (info.bmiHeader.biCompression != '462H' && info.bmiHeader.biCompression != '462h' &&
		info.bmiHeader.biCompression != '462X' && info.bmiHeader.biCompression != '462x')
		return E_FAIL;

	_width = info.bmiHeader.biWidth;
	_height = info.bmiHeader.biHeight;

    // Is this a palettised format

	//if(CanChangeContrastLevel(mtIn))
 //   {
        return NOERROR;
    //}

    //return E_FAIL;

} // CheckInputType

bool H264DecodeFilter::IsConnected()
{
	IPin *connectPin;
	if (m_pInput->ConnectedTo(&connectPin) == VFW_E_NOT_CONNECTED)
	{
		return false;
	}
	else
	{
		connectPin->Release();
		return true;
	}

}

//
// CheckTransform
//
// To be able to transform the formats must be identical
//
HRESULT H264DecodeFilter::CheckTransform(const CMediaType *mtIn,const CMediaType *mtOut)
{
    CheckPointer(mtIn,E_POINTER);
    CheckPointer(mtOut,E_POINTER);

    HRESULT hr;
    if(FAILED(hr = CheckInputType(mtIn)))
    {
        return hr;
    }

    // format must be a VIDEOINFOHEADER
    if(*mtOut->FormatType() != FORMAT_VideoInfo)
    {
        return E_INVALIDARG;
    }

    // formats must be big enough 
    if(mtIn->FormatLength() < sizeof(VIDEOINFOHEADER) ||
        mtOut->FormatLength() < sizeof(VIDEOINFOHEADER))
        return E_INVALIDARG;

    VIDEOINFO *pInput  = (VIDEOINFO *) mtIn->Format();
    VIDEOINFO *pOutput = (VIDEOINFO *) mtOut->Format();

	CopyMediaType(&m_intype, mtIn);
	CopyMediaType(&m_outtype, mtOut);

    //if(memcmp(&pInput->bmiHeader,&pOutput->bmiHeader,sizeof(BITMAPINFOHEADER)) == 0)
    //{
        return NOERROR;
    //}

    //return E_INVALIDARG;

} // CheckTransform


//
// DecideBufferSize
//
// Tell the output pin's allocator what size buffers we
// require. Can only do this when the input is connected
//
HRESULT H264DecodeFilter::DecideBufferSize(IMemAllocator *pAlloc,ALLOCATOR_PROPERTIES *pProperties)
{
    CheckPointer(pAlloc,E_POINTER);
    CheckPointer(pProperties,E_POINTER);

    // Is the input pin connected

    if(m_pInput->IsConnected() == FALSE)
    {
        return E_UNEXPECTED;
    }

    HRESULT hr = NOERROR;
    pProperties->cBuffers = 1;

	CMediaType &mediaType = m_pInput->CurrentMediaType();

	VIDEOINFO pvi;

	// Initialize the VideoInfo structure before configuring its members
	memcpy(&pvi, mediaType.Format(), mediaType.FormatLength());// sizeof(VIDEOINFO));
	pvi.dwBitErrorRate = 0;
	pvi.dwBitRate = 0;
	pvi.AvgTimePerFrame = 0;

	pvi.bmiHeader.biCompression = 'VUYI';
	pvi.bmiHeader.biBitCount    = 12;

	pvi.bmiHeader.biPlanes     = 3;
	pProperties->cbBuffer  = GetBitmapSize(&pvi.bmiHeader) + 4096;

    //pProperties->cbBuffer = m_pInput->CurrentMediaType().GetSampleSize();

    //ASSERT(pProperties->cbBuffer);

    //// If we don't have fixed sized samples we must guess some size

    //if(!m_pInput->CurrentMediaType().bFixedSizeSamples)
    //{
    //    if(pProperties->cbBuffer < 100000)
    //    {
    //        // nothing more than a guess!!
    //        pProperties->cbBuffer = 100000;
    //    }
    //}

    // Ask the allocator to reserve us some sample memory, NOTE the function
    // can succeed (that is return NOERROR) but still not have allocated the
    // memory that we requested, so we must check we got whatever we wanted

    ALLOCATOR_PROPERTIES Actual;

    hr = pAlloc->SetProperties(pProperties,&Actual);
    if(FAILED(hr))
    {
        return hr;
    }

    ASSERT(Actual.cBuffers == 1);

    if(pProperties->cBuffers > Actual.cBuffers ||
        pProperties->cbBuffer > Actual.cbBuffer)
    {
        return E_FAIL;
    }

    return NOERROR;

} // DecideBufferSize


//
// GetMediaType
//
// I support one type, namely the type of the input pin
// We must be connected to support the single output type
//
HRESULT H264DecodeFilter::GetMediaType(int iPosition, CMediaType *pMediaType)
{
    // Is the input pin connected

    if(m_pInput->IsConnected() == FALSE)
    {
        return E_UNEXPECTED;
    }

    // This should never happen

    if(iPosition < 0)
    {
        return E_INVALIDARG;
    }

    // Do we have more items to offer

    if(iPosition > 0)
    {
        return VFW_S_NO_MORE_ITEMS;
    }

    CheckPointer(pMediaType,E_POINTER);

	CMediaType &mediaType = m_pInput->CurrentMediaType();

	VIDEOINFO *pvi = (VIDEOINFO *) pMediaType->AllocFormatBuffer(sizeof(VIDEOINFO));
	if(NULL == pvi)
		return(E_OUTOFMEMORY);

	// Initialize the VideoInfo structure before configuring its members
	memcpy(pvi, mediaType.Format(), mediaType.FormatLength());//sizeof(VIDEOINFO));
	pvi->dwBitErrorRate = 0;
	pvi->dwBitRate = 0;
	pvi->AvgTimePerFrame = 0;

	pvi->bmiHeader.biCompression = '21VY';
	pvi->bmiHeader.biBitCount    = 12;

	// Adjust the parameters common to all formats
	pvi->bmiHeader.biSize       = sizeof(BITMAPINFOHEADER);
	pvi->bmiHeader.biPlanes     = 3;
	pvi->bmiHeader.biSizeImage  = 0; //GetBitmapSize(&pvi->bmiHeader);
	pvi->bmiHeader.biClrImportant = 0;

	//SetRectEmpty(&(pvi->rcSource)); // we want the whole image area rendered.
	//SetRectEmpty(&(pvi->rcTarget)); // no particular destination rectangle

	pMediaType->SetType(&MEDIATYPE_Video);
	pMediaType->SetFormatType(&FORMAT_VideoInfo);
	pMediaType->SetTemporalCompression(FALSE);

	// Work out the GUID for the subtype from the header info.
	pMediaType->SetSubtype(&MEDIASUBTYPE_YV12);
	pMediaType->SetSampleSize(pvi->bmiHeader.biSizeImage);    
	
	return NOERROR;

} // GetMediaType

#endif



